﻿
namespace Patterns4Net.Tests.Core.CecilExtensions.CILPatternsMatching
{
    using System;
    using Mono.Cecil.Cil;
    using Moq;
    using NUnit.Framework;
    using Patterns4Net.Core.CecilExtensions.CILPatternsMatching;

    [TestFixture]
    public class FreeFormInstructionMatcherTest
    {
        private Mock<InstructionMatcher> instrMatcher;

        private Instruction instrMatcherNextInstr;

        private Instruction startInstruction;

        private FreeFormInstructionMatcher matcher;

        private Instruction startInstructionNext;

        [Test]
        public void When_Internal_Matcher_Does_Not_Match_It_Jumps_To_Next_And_Returns_True()
        {
            this.SetUp(false);
            Instruction actualNextInstruction;
            bool result = this.matcher.Matches(this.startInstruction, out actualNextInstruction);

            Assert.IsTrue(result, "result should be true.");
            Assert.IsFalse(this.matcher.Found, "matcher.Found should be false");
            Assert.AreEqual(this.startInstructionNext, actualNextInstruction);
            this.instrMatcher.Verify();
        }

        [Test]
        public void When_Internal_Matcher_Matches_It_Jumps_To_Its_Next_And_Returns_True()
        {
            this.SetUp(true);
            Instruction actualNextInstruction;
            bool result = this.matcher.Matches(this.startInstruction, out actualNextInstruction);

            Assert.IsTrue(result, "result should be true");
            Assert.IsTrue(this.matcher.Found, "matcher.Found should be true.");
            Assert.AreEqual(this.instrMatcherNextInstr, actualNextInstruction);
            this.instrMatcher.Verify();
        }

        public void SetUp(bool internalMatcherResult)
        {
            this.instrMatcher = new Mock<InstructionMatcher>();
            this.instrMatcherNextInstr = Instruction.Create(OpCodes.Ldc_I4_0);
            this.startInstruction = Instruction.Create(OpCodes.Ldc_I4_1);
            this.instrMatcher.Setup(x =>
                    x.Matches(
                        It.Is<Instruction>(i => i.OpCode == this.startInstruction.OpCode),
                        out this.instrMatcherNextInstr))
                .Returns(internalMatcherResult)
                .Verifiable();

            // We have to return false for Code Contracts in ctor Ensures
            bool[] isCheckedByCodeContarcts = { true };
            this.instrMatcher.SetupGet(x => x.Found)
                .Returns(() => !isCheckedByCodeContarcts[0] && internalMatcherResult);
            var instrMatcherObject = this.instrMatcher.Object;
            bool needToCallFoundGetter = instrMatcherObject.Found;
            if (needToCallFoundGetter)
            {
                Console.WriteLine("Avoid clever compiler stipe out the variable. This never happens.");
            }

            isCheckedByCodeContarcts[0] = false;
            this.matcher = new FreeFormInstructionMatcher(instrMatcherObject);
            this.startInstructionNext = Instruction.Create(OpCodes.Ldc_I4_4);
            this.startInstruction.Next = this.startInstructionNext;
        }
    }
}
